# FlowRadar.py
# 赵宗义, xinshengzzy@foxmail.com

from simulators.BaseSimulator import BaseSimulator
import mytools
import 文件工具
from simulators.Buckets import FRRecord
from simulators.Bitmap import Bitmap
from network.通用工具 import 标识符转字节流, 字节流转标识符, 字节流异或
from 标准库 import 累积序列


class FlowRadar(BaseSimulator):
    def __init__(self, 内存=1.0, 协议="ipv4"):
        super(FlowRadar, self).__init__()
        self.内存 = 内存
        self.协议 = 协议
        self.计算表长()
        self.辅表 = Bitmap(self.表长1)
        self.主表 = []
        for 索引 in range(self.表长1 + self.表长2 + self.表长3):
            self.主表.append(FRRecord())  # (digest, num. of flows, num. of pkts)

    def 计算表长(self):
        if "ipv4" == self.协议:
            临变 = int(self.内存*1024.0*1024.0/26)
        elif "ipv6" == self.协议:
            临变 = int(self.内存*1024.0*1024.0/50.0)
        else:
            raise SystemExit()
        self.表长1 = 临变*40
        self.表长2 = int(临变/3)
        self.表长3 = int(临变/3)
        self.表长4 = int(临变/3)

    def 计算导出周期(self, 带宽消耗):
        if "ipv4" == self.协议:
            哈希桶宽度 = 21.0*8.0
        elif "ipv6" == self.协议:
            哈希桶宽度 = 45.0*8.0
        self.导出周期 = (哈希桶宽度 * (self.表长2 + self.表长3 + self.表长4))/带宽消耗

    def 周期式数据包处理(self, 标识符, 时间戳):
        索引 = self.计算索引(标识符)
        # 查询并更新辅表
        已存在 = True
        for i in range(4):
            if not self.辅表.test(索引[i]):
                已存在 = False
                self.辅表.set(索引[i])
        # 更新主表
        for i in range(4, 7):
            if 已存在:
                self.主表[索引[i]].increase()
            else:
                self.主表[索引[i]].add(标识符)

    def 在线流查询(self, 标识符):
        pass

    def 计算索引(self, 标识符):
        偏移量 = 累积序列([0, self.表长2, self.表长3])
        idx1 = mytools.hash1(标识符.encode("utf-8")) % self.表长1
        idx2 = mytools.hash2(标识符.encode("utf-8")) % self.表长1
        idx3 = mytools.hash3(标识符.encode("utf-8")) % self.表长1
        idx4 = mytools.hash4(标识符.encode("utf-8")) % self.表长1
        idx5 = mytools.hash5(标识符.encode("utf-8")) % self.表长2 + 偏移量[0]
        idx6 = mytools.hash6(标识符.encode("utf-8")) % self.表长3 + 偏移量[1]
        idx7 = mytools.hash7(标识符.encode("utf-8")) % self.表长4 + 偏移量[2]
        return [idx1, idx2, idx3, idx4, idx5, idx6, idx7]

    def 下载数据平面(self):
        流集 = dict()
        可解码单元 = set()
        for 索引 in range(self.表长2 + self.表长3 + self.表长4):
            if 1 == self.主表[索引].getNFlows():
                可解码单元.add(索引)
        while len(可解码单元) > 0:
            索引 = 可解码单元.pop()
            字节流 = self.主表[索引].getFlowset()
            标识符 = 字节流转标识符(字节流)
            流长度 = self.主表[索引].getNPkts()
            流集[标识符] = 流长度
            索引序列 = self.计算索引(标识符)
            self.主表[索引序列[4]].update(字节流, -1, -流长度)
            self.主表[索引序列[5]].update(字节流, -1, -流长度)
            self.主表[索引序列[6]].update(字节流, -1, -流长度)
            if 1 == self.主表[索引序列[4]].getNFlows():
                可解码单元.add(索引序列[4])
            if 1 == self.主表[索引序列[5]].getNFlows():
                可解码单元.add(索引序列[5])
            if 1 == self.主表[索引序列[6]].getNFlows():
                可解码单元.add(索引序列[6])
        return 流集

    def 重置数据平面(self):
        self.辅表.reset()
        for i in range(self.表长2 + self.表长3 + self.表长4):
            self.主表[i].reset()

    def 备份(self, 文件名):
        备份 = dict()
        备份["流记录集"] = self.获取流记录集()
        文件工具.备份数据(备份, 文件名)
